<!--
*@Author: QWP
*@Description: 表单控件
*@Date: 2023-06-18 16:03:29
-->
<template>
  <a-spin tip="Loading..."
    :spinning="loading">
    <a-form
      v-bind="$attrs"
      :rules="getRules"
      ref="form"
      class="u-common-form">
      <a-row :gutter="props.gutter"
        :wrap="props.wrap">
        <template v-for="(fItem, index) in props.elements"
          :key="fItem.key ?? index" >
          <a-col v-bind="fItem.col || {}"
            v-if="fItem['hide'] != true">
            <a-form-item
              :name="fItem.key"
              :label="fItem.label"
              :style="setExtraAttr(fItem.formItemProps, $attrs['style'] || {})"
              :validateFirst="true"
              v-bind="fItem.formItemProps">
              
              <template v-if="fItem.element == 'slot'">
                <slot :name="fItem.key" :formItemProps="fItem.formItemProps" :componentProps="fItem.componentProps"></slot>
              </template>

              <template v-else>
                <component
                  :is="componentsMap[fItem.element]"
                  :autofocus="false"
                  :disabled="$attrs['disabled'] || false"
                  :allowClear="$attrs['allowClear'] || true"
                  autocomplete="off"
                  :fieldKey="fItem.key"
                  v-bind="initElProps(fItem.element, fItem.componentProps)"
                  v-model:value="$attrs.model[fItem.key]"
                  v-model:checked="$attrs.model[fItem.key]"
                  :placeholder="getPlaceHolder(fItem, fItem.componentProps)"
                  @change="(e, options) => formChange(fItem, fItem.componentProps, index, e, options)"
                  @pressEnter="formPressEnter(fItem)"
                  @click="click(fItem.key, $attrs.model[fItem.key], fItem)"
                  @toValid="toValid">
                  <template v-if="fItem.element == 'text'">
                    {{ $attrs.model[fItem.key] }}
                  </template>
                </component>
              </template>
            </a-form-item>
          </a-col>
        </template>
        <slot></slot>
      </a-row>
      <slot name="outRow"></slot>
    </a-form>
  </a-spin>
</template>

<script lang="ts" setup>
defineOptions({
  name: "u-form",
});
import { ref, reactive, computed, CSSProperties, useAttrs } from "vue";
import type { Rule } from "ant-design-vue/es/form";
import { componentsMap } from "./config.ts";
import { _IFormEl } from './interface.ts'
import { cloneDeep } from 'lodash'

interface Props {
  elements: Array<_IFormEl>;
  rule?: Record<string, Rule[]>;
  gutter?: number;
  wrap?: boolean;
  loading?: boolean;
  isSearch?: boolean
}
const props = withDefaults(defineProps<Props>(), {
  gutter: 12,
  wrap: true,
  loading: false,
  isSearch: false,
});
const attrs = useAttrs()

const emits = defineEmits(["click", "formChange", 'enter', 'onChange']);
const form = ref<FormInstance>();
defineExpose({
  form,
});

// 获取校验规则
const getRules = computed(() => {
  let rules = props.rule || {};
  props.elements.forEach((item) => {
    let formItemProps = item.formItemProps || {}
    if (!rules[item.key]) {
      rules[item.key] = [];
    }
    if (formItemProps["required"] == true) {
      rules[item.key].push({
        required: true,
        message: item.label + "不能为空!",
        trigger:   ["blur", "change"],
      });
    }
  });
  return rules;
});

// 控件focus事件
const click = (key, value, formItem) => {
  let componentProps = formItem['componentProps'] || {};
  if(attrs['disabled'] == true || componentProps['disabled'] == true) {
    return
  }
  emits("click", { key, value });
};

// 设置属性
const initElProps = (element: string, componentProps): object => {
  let compProps = cloneDeep(componentProps || {});
  const defaultProps: {
    [key: string]: objet | any
  } = {
    datepicker: {
      valueFormat: 'YYYY-MM-DD'
    }
  }

  if(compProps['onChange']) {
    delete compProps['onChange']
  }

  let tProps = defaultProps[element] || {};
  return { ...tProps, ...compProps }
};

// 设置额外的属性
const setExtraAttr = (formItemProps, style) => {
  let tStyle = {}
  if(formItemProps) {
    if(formItemProps['labelWidth']) {
      tStyle['--lw'] = `${formItemProps['labelWidth']}`.replace('px', '') + 'px';
    }
  }

  return { ...tStyle, ...style};
}

// 设置提示词
const getPlaceHolder = (fItem, componentProps) => {
  if(['input', 'password', 'textarea', 'number', 'money'].includes(fItem.element)) {
    return (componentProps || {})['placeholder'] || `请输入${fItem.label}`
  } 
  else if(['cascader', 'checkbox', 'CheckboxGroup', 'radio', 'select', 'switch', 'treeselect'].includes(fItem.element)) {
    return (componentProps || {})['placeholder'] || `请选择${fItem.label}`
  }
}

// 表单控件change事件
const formChange = (fItem, componentProps, elIndex, event, options) => {
  const titem = fItem || {};

  // 将change事件返回
  if((componentProps ?? {}).hasOwnProperty('onChange')) { 
    props.elements[elIndex].componentProps.onChange.call(props.elements[elIndex].componentProps, event, options)
  }

  if(props.isSearch == true) {
    if(titem.element != 'input') {
      emits('formChange', { key: titem.key, value: attrs.model[titem.key] })
    }
  } 
  else {
    emits('formChange', { key: titem.key, value: attrs.model[titem.key] })
  }
}

// 表单控件回车事件
const formPressEnter = (fItem) => {
  const titem = fItem || {}
  emits('formChange', { key: titem.key, value: attrs.model[titem.key] })
}

// 上传验证
const toValid = (key: string) => {
  try {
    form.value.validateFields([key])
    form.value.clearValidate([key])
  } catch(e) {}
}

</script>

<style lang="scss">
.u-common-form {
  .ant-form-item {
    margin-bottom: 12px;
    .ant-form-item-row[labelWidth] {
      $width: var(--lw);
      .ant-form-item-label {
        width: $width
      }
    }
  }
  .ant-input-textarea-show-count::after {
    position: absolute;
    bottom: 0;
    right: 10px;
    z-index: 2;
  }
}
.ant-picker, 
.ant-input-number, 
.ant-input-number-affix-wrapper, 
.ant-input-number-group-wrapper,
.ant-select {
  width: 100%
}
.ant-select-disabled:where(.css-dev-only-do-not-override-1x77r0v)
.ant-select:not(.ant-select-customize-input) 
.ant-select-selector{
  color: rgba(0, 0, 0, 0.75);
  background: rgba(0, 0, 0, 0.02);
}
.ant-select-disabled.ant-select-multiple .ant-select-selection-item,
.ant-select-selection-item,
.ant-picker-input >input[disabled]  {
  color: rgba(0, 0, 0, 0.75) !important;
}
.ant-select-disabled {
  // background: rgba(0, 0, 0, 0.02) !important;
}
.ant-input-disabled{
  color: rgba(0, 0, 0, 0.75) !important;
  // background: rgba(0, 0, 0, 0.02) !important;
}



</style>
